use std::{
    sync::{Arc, LazyLock},
    time::SystemTime,
};

use chrono::{DateTime, Datelike, Local, Timelike};
use parking_lot::RwLock;

#[allow(dead_code)]
#[inline]
pub fn get_now_msec() -> u64 {
    let now = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap();
    now.as_millis() as u64
}

pub struct CacheTime {
    cache_time_ms: u64,
    cache_log_time: Arc<String>,
    cache_http_date: Arc<String>,
}

fn to_http_date_string(epoch_sec: i64) -> Arc<String> {
    let dt = DateTime::from_timestamp(epoch_sec, 0).unwrap();
    Arc::new(dt.format("%a, %d %b %Y %H:%M:%S GMT").to_string())
}

fn to_log_time_string() -> Arc<String> {
    let now_time = Local::now();
    Arc::new(format!(
        "{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
        now_time.year(),
        now_time.month(),
        now_time.day(),
        now_time.hour(),
        now_time.minute(),
        now_time.second()
    ))
}

#[allow(dead_code)]
impl CacheTime {
    pub fn get_instance() -> &'static RwLock<CacheTime> {
        static CACHE_TIME: LazyLock<RwLock<CacheTime>> = LazyLock::new(|| {
            RwLock::new(CacheTime {
                cache_time_ms: 0,
                cache_log_time: Arc::new("".to_string()),
                cache_http_date: Arc::new("".to_string()),
            })
        });

        &CACHE_TIME
    }

    // 经过测试, get_log_time比使用DateTime format性能要高7倍以上.
    // 如下在release模式下测试:
    // get_log_time: 10000000/0.638 r/s
    // format Local::now(): 10000000/4.085 r/s
    pub fn get_log_time() -> Arc<String> {
        let now = get_now_msec();
        let cache_time = Self::get_instance();
        {
            let read = cache_time.read();
            if now - read.cache_time_ms < 200 {
                return read.cache_log_time.clone();
            }
        }

        let http_date = to_http_date_string((now / 1000) as i64);
        let log_time = to_log_time_string();

        let mut write = cache_time.write();
        write.cache_time_ms = now;
        write.cache_log_time = log_time.clone();
        write.cache_http_date = http_date;

        log_time
    }

    pub fn get_http_date() -> Arc<String> {
        let now = get_now_msec();
        let cache_time = Self::get_instance();
        {
            let read = cache_time.read();
            if now - read.cache_time_ms < 200 {
                return read.cache_log_time.clone();
            }
        }

        let http_date = to_http_date_string((now / 1000) as i64);
        let log_time = to_log_time_string();

        let mut write = cache_time.write();
        write.cache_time_ms = now;
        write.cache_log_time = log_time;
        write.cache_http_date = http_date.clone();

        http_date
    }
}

#[cfg(test)]
mod tests {
    use chrono::{Datelike, Local, Timelike};

    use super::{get_now_msec, CacheTime};

    #[test]
    pub fn test_log_time() {
        let st = get_now_msec();

        for _ in 0..1000000 {
            let time = CacheTime::get_log_time();
            _ = time;
        }

        let interval = get_now_msec() - st;
        println!("interval: {}", interval);

        let st = get_now_msec();
        for _ in 0..1000000 {
            let now_time = Local::now();
            let log_time = format!(
                "{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
                now_time.year(),
                now_time.month(),
                now_time.day(),
                now_time.hour(),
                now_time.minute(),
                now_time.second()
            );
            _ = log_time;
        }
        let interval = get_now_msec() - st;
        println!("interval: {}", interval);

        println!("{}", CacheTime::get_http_date());
        println!("{}", CacheTime::get_log_time());
    }
}
